﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace NetWatch.ATest
{
    public delegate void TestStateChangedDelegate(ATest sender, ETestState state);

    /// <summary>
    /// Abstract parent for all tests
    /// </summary>
    public abstract class ATest
    {
        #region Abstract

        public abstract bool Test();
        protected abstract bool LoadTestConfig(Dictionary<string, object> valueList);

        #endregion

        #region Virtual

        public virtual Dictionary<string, Type> ConfigValues { get { return new Dictionary<string, Type>(); } }

        public virtual string Info { get { return string.Empty; } }

        #endregion

        #region Events

        public event TestStateChangedDelegate TestStateChanged;
        public event TestStateChangedDelegate TestFinished;

        #endregion

        #region Variabled

        public ETestState LastState = ETestState.Ok;

        public int AttemptsToOk = 1;
        public int AttemptsToError = 1;
        public int AttemptsToErrorUrgent = 5;

        /// <summary>[s] Test period when test is in OK state.</summary>
        public int PeriodOk = 5;
        /// <summary>[s] Test period when test is not in OK state.</summary>
        public int PeriodError = 1;


        private Timer timer = null;

        #endregion

        #region Properties

        public bool Enabled { get { return this.timer.Enabled; } set { this.timer.Enabled = value; } }

        #endregion

        #region Implementation

        public ATest()
        {
            this.timer = new Timer();
            this.timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
        }


        public bool LoadConfig(Dictionary<string, object> valueList)
        {
            if(valueList == null || valueList.Count ==0)
                return false;

            if (valueList.ContainsKey("AttemptsToOk")) this.AttemptsToOk = (int)valueList["AttemptsToOk"];
            if (valueList.ContainsKey("AttemptsToError")) this.AttemptsToError = (int)valueList["AttemptsToError"];
            if (valueList.ContainsKey("AttemptsToErrorUrgent")) this.AttemptsToErrorUrgent = (int)valueList["AttemptsToErrorUrgent"];

            if (valueList.ContainsKey("PeriodOk")) this.PeriodOk = (int)valueList["PeriodOk"];
            if (valueList.ContainsKey("PeriodError")) this.PeriodError = (int)valueList["PeriodError"];

            return LoadTestConfig(valueList);
        }

        public void Start() 
        {
            timer_Elapsed(this, null);
            this.timer.Enabled = true; 
        }

        public void Stop() { this.timer.Enabled = false; }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            bool bResult = true;

            try { bResult = Test(); }
            catch { bResult = false; }


            ETestState result = ETestState.Ok;
            if (!bResult)
                result =
                    (this.LastState == ETestState.Ok)
                    ? ETestState.Error
                    : ETestState.ErrorUrgent;

            switch (result)
            {
                case ETestState.Ok:
                    this.timer.Interval = this.PeriodOk * 1000;
                    break;

                case ETestState.Error:
                case ETestState.ErrorUrgent:
                    this.timer.Interval = this.PeriodError * 1000;
                    break;
            }

            ETestState lastState = this.LastState;
            this.LastState = result;

            if (this.TestFinished != null)
                this.TestFinished(this, result);


            if (lastState != result && this.TestStateChanged != null)
                this.TestStateChanged(this, result);
        }

        #endregion
    }
}
